﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="DatabasesViewModel.cs" company="Marcin Jastrzebski">
//   Copyright (c) Marcin Jastrzebski (marcin@marcinj.net)
// </copyright>
// <summary>
//   The databases page view model.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace mPassRT.ViewModels.Databases
{
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.IO;
    using System.Threading.Tasks;
    using System.Windows.Input;

    using KeePassLib;
    using KeePassLib.Interfaces;
    using KeePassLib.Keys;
    using KeePassLib.Serialization;

    using Microsoft.Practices.Unity;

    using mPassRT.Common;
    using mPassRT.Pages;
    using mPassRT.Services;

    using Windows.Storage;
    using Windows.Storage.Pickers;
    using Windows.UI.Popups;

    /// <summary>The databases page view model.</summary>
    public class DatabasesViewModel : PageViewModel
    {
        #region Constants

        /// <summary>The database file extension.</summary>
        private const string DatabaseFileExtension = ".kdbx";

        #endregion

        #region Fields

        /// <summary>The database manager.</summary>
        private readonly IDatabaseManager databaseManager;

        /// <summary>The selected databases.</summary>
        private IList<DatabaseViewModel> selectedDatabases = new List<DatabaseViewModel>();

        #endregion

        #region Constructors and Destructors

        /// <summary>Initializes a new instance of the <see cref="DatabasesViewModel"/> class.</summary>
        /// <param name="databaseManager">The database Manager.</param>
        public DatabasesViewModel([Dependency] IDatabaseManager databaseManager)
        {
            this.Databases = new ObservableCollection<BindableBase>();
            this.databaseManager = databaseManager;

            foreach (Database database in databaseManager.Databases)
            {
                this.Databases.Add(new DatabaseViewModel(database));
            }

            this.Databases.Add(new NewItemViewModel());
        }

        #endregion

        #region Public Properties

        /// <summary>Gets the add command.</summary>
        public ICommand AddExistingCommand
        {
            get
            {
                return new Command { ExecuteHandler = this.AddExistingFile };
            }
        }

        /// <summary>Gets the add new command.</summary>
        public ICommand AddNewCommand
        {
            get
            {
                return new Command { ExecuteHandler = this.AddNewFile };
            }
        }

        /// <summary>Gets a value indicating whether are database selected.</summary>
        public bool AreDatabasesSelected
        {
            get
            {
                return this.SelectedDatabases.Count > 0;
            }
        }

        /// <summary>Gets the databases.</summary>
        public ObservableCollection<BindableBase> Databases { get; private set; }

        /// <summary>Gets or sets the key provider.</summary>
        [Dependency]
        public IKeyProvider KeyProvider { get; set; }

        /// <summary>Gets or sets the navigator.</summary>
        [Dependency]
        public INavigator Navigator { get; set; }

        /// <summary>Gets the open command.</summary>
        public ICommand OpenCommand
        {
            get
            {
                return new Command { ExecuteHandler = this.Open };
            }
        }

        /// <summary>Gets the remove command.</summary>
        public ICommand RemoveCommand
        {
            get
            {
                return new Command { ExecuteHandler = this.RemoveFile };
            }
        }

        /// <summary>Gets or sets the selected databases.</summary>
        public IList<DatabaseViewModel> SelectedDatabases
        {
            get
            {
                return this.selectedDatabases;
            }

            set
            {
                this.SetProperty(ref this.selectedDatabases, value);
                this.OnPropertyChanged("AreDatabasesSelected");
            }
        }

        #endregion

        #region Methods

        /// <summary>Adds the database to the list.</summary>
        /// <param name="file">The file.</param>
        /// <returns>The <see cref="Task"/>.</returns>
        private async Task AddDatabaseInternal(StorageFile file)
        {
            string errorMessage = null;
            try
            {
                Database db = this.databaseManager.AddDatabase(file);
                this.Databases.Insert(this.Databases.Count - 1, new DatabaseViewModel(db));
            }
            catch (InvalidOperationException exception)
            {
                errorMessage = exception.Message;
            }

            if (errorMessage != null)
            {
                await new MessageDialog(errorMessage).ShowAsync();
            }
        }

        /// <summary>Adds a file to the list of databases.</summary>
        /// <param name="arg">The parameter is not used.</param>
        private async void AddExistingFile(object arg)
        {
            var picker = new FileOpenPicker
                             {
                                 CommitButtonText = "Add", 
                                 ViewMode = PickerViewMode.List, 
                                 SettingsIdentifier = "DatabasesViewModel_AddExisting", 
                                 SuggestedStartLocation = PickerLocationId.DocumentsLibrary
                             };

            picker.FileTypeFilter.Add(DatabaseFileExtension);
            StorageFile file = await picker.PickSingleFileAsync();
            if (file != null)
            {
                await this.AddDatabaseInternal(file);
            }
        }

        /// <summary>Adds new file.</summary>
        /// <param name="obj">The parameter is not used.</param>
        private async void AddNewFile(object obj)
        {
            var picker = new FileSavePicker
                             {
                                 CommitButtonText = "Create", 
                                 DefaultFileExtension = DatabaseFileExtension, 
                                 SettingsIdentifier = "DatabasesViewModel_AddNew", 
                                 SuggestedStartLocation = PickerLocationId.DocumentsLibrary
                             };
            picker.FileTypeChoices.Add("KeePass KDBX file", new List<string> { DatabaseFileExtension });

            StorageFile file = await picker.PickSaveFileAsync();
            if (file != null)
            {
                CompositeKey key = await this.KeyProvider.GetKey(file.Path, KeyOperation.Create);
                if (key != null)
                {
                    await this.AddDatabaseInternal(file);

                    var database = new PwDatabase();
                    database.New(IOConnectionInfo.FromPath(file.Path), key);
                    database.Name = Path.GetFileNameWithoutExtension(file.Path);

                    database.Save(new NullStatusLogger());
                }
                else
                {
                    // user cancelled, we need to delete the file
                    // since it's a 0-size file that we just created
                    // we don't need to move it to the recycle bin
                    await file.DeleteAsync(StorageDeleteOption.PermanentDelete);
                }
            }
        }

        /// <summary>Opens the database.</summary>
        /// <param name="obj">The object.</param>
        private void Open(object obj)
        {
            var selectedDb = (DatabaseViewModel)obj;
            this.Navigator.NavigateTo(
                typeof(GroupPage), new DatabaseNavigationParameter { FilePath = selectedDb.Database.File.Path });
        }

        /// <summary>Removes the file from list of databases.</summary>
        /// <param name="obj">The object.</param>
        private void RemoveFile(object obj)
        {
            foreach (DatabaseViewModel database in this.SelectedDatabases)
            {
                this.Databases.Remove(database);
                this.databaseManager.RemoveDatabase(database.Database);
            }
        }

        #endregion
    }
}